using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SqlServer.Dts.Runtime;
using System.Xml;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;

namespace Microsoft.SharedSource.SqlServer.MDDE.Components
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1705:LongAcronymsShouldBePascalCased")]
    public abstract class MDDEDataFlowComponent : MDDEComponent
    {
        public IDTSComponentMetaData90 InnerDataFlowComponent
        {
            get { return (IDTSComponentMetaData90)this.innerComponent; }
        }

        public MDDEDataFlowComponent(Package package, Object component, string name, string uri)
            : base(package, component, name, uri)
        {
        }

        #region Overridden Methods

        public override System.Xml.XmlDocument GetComponentTree()
        {
            XmlDocument xComponent = new XmlDocument();

            // Create the 'Node' node.
            XmlElement xRoot = AppendChildElement(xComponent, "Node",
                new string[]{ "Type", "Component", "Name", InnerDataFlowComponent.Name,
                    "Uri", Uri, 
                    "ClassId", InnerDataFlowComponent.ComponentClassID,
                    "RowsetDescriptorType", string.Empty, "ContainsMappings", "False",
                    "ContainsCollections", "False" });

            // Append the properties.
            foreach (IDTSCustomProperty90 prop in InnerDataFlowComponent.CustomPropertyCollection)
                AppendChildElement(xRoot, "Property",
                    new string[] { "Name", prop.Name, 
                        "Value",  prop.Value == null ?  
                        string.Empty :  prop.Value.ToString() });

            #region Append Inputs.

            if (InnerDataFlowComponent.InputCollection.Count > 0)
            {
                // Create an 'Inputs' collection node.
                XmlElement xnodeInputs =
                    AppendChildElement(xRoot, "Node",
                    new string[]{ "Type", "Collection", "Name", "Inputs",
                    "Uri", Uri + "/Inputs", "ClassId", string.Empty,
                    "RowsetDescriptorType", string.Empty, "ContainsMappings", "False",
                    "ContainsCollections", "False" });

                foreach (IDTSInput90 input in InnerDataFlowComponent.InputCollection)
                {
                    XmlElement xnodeInput =
                    AppendChildElement(xnodeInputs, "Node",
                    new string[]{ "Type", "Input", "Name", input.Name,
                        "Uri", Uri + "/Input=" + input.Name, 
                        "ClassId", "Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSInput90",
                        "RowsetDescriptorType", string.Empty, "ContainsMappings", "False",
                        "ContainsCollections", "False" });

                    // Append input properties.
                    foreach (IDTSCustomProperty90 prop in input.CustomPropertyCollection)
                        AppendChildElement(xnodeInput, "Property",
                            new string[]{"Name", prop.Name, "Value", 
                                prop.Value == null ? string.Empty :prop.Value.ToString()});

                    // Create a 'Columns collection node.
                    if (input.InputColumnCollection.Count > 0)
                    {

                        XmlElement xnodeInputCols =
                            AppendChildElement(xnodeInput, "Node",
                            new string[]{ "Type", "Collection", "Name", "Columns",
                                "Uri", Uri + "/Input=" + input.Name + "/Columns", "ClassId", string.Empty,
                                "RowsetDescriptorType", string.Empty, "ContainsMappings", "False",
                                "ContainsCollections", "False" });

                        foreach (IDTSInputColumn90 inCol in
                            input.InputColumnCollection)
                        {
                            // Add the 'Column' node.
                            XmlElement xCol = AppendChildElement(xnodeInputCols, "Node",
                                new string[]{ "Type", "Column", "Name", inCol.Name,
                                    "Uri", Uri + "/Input=" + input.Name + "/Column=" + inCol.Name, 
                                    "ClassId", 
                                    "Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSInputColumn90",
                                    "RowsetDescriptorType", string.Empty, "ContainsMappings", "False",
                                    "ContainsCollections", "False" });

                            // Append column properties.
                            foreach (IDTSCustomProperty90 prop in inCol.CustomPropertyCollection)
                                AppendChildElement(xCol, "Property",
                                    new string[]{"Name", prop.Name, "Value", 
                                prop.Value == null ? string.Empty :prop.Value.ToString()});
                        }
                    }
                }
            }

            #endregion

            #region Append Outputs.

            if (InnerDataFlowComponent.OutputCollection.Count > 0)
            {
                // Create an 'Outputs' collection node.
                XmlElement xnodeOutputs =
                    AppendChildElement(xRoot, "Node",
                    new string[]{ "Type", "Collection", "Name", "Outputs",
                    "Uri", Uri + "/Outputs", "ClassId", string.Empty,
                    "RowsetDescriptorType", string.Empty, "ContainsMappings", "False",
                    "ContainsCollections", "False" });

                foreach (IDTSOutput90 output in InnerDataFlowComponent.OutputCollection)
                {
                    XmlElement xnodeOutput =
                    AppendChildElement(xnodeOutputs, "Node",
                    new string[]{ "Type", "Output", "Name", output.Name,
                        "Uri", Uri + "/Output=" + output.Name, 
                        "ClassId", "Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSOutput90",
                        "RowsetDescriptorType", string.Empty, "ContainsMappings", "False",
                        "ContainsCollections", "False" });

                    // Append output properties.
                    foreach (IDTSCustomProperty90 prop in output.CustomPropertyCollection)
                        AppendChildElement(xnodeOutput, "Property",
                            new string[]{"Name", prop.Name, "Value", 
                                prop.Value == null ? string.Empty :prop.Value.ToString()});

                    // Create a 'Columns collection node.
                    if (output.OutputColumnCollection.Count > 0)
                    {

                        XmlElement xnodeOutputCols =
                            AppendChildElement(xnodeOutput, "Node",
                            new string[]{ "Type", "Collection", "Name", "Columns",
                                "Uri", Uri + "/Output=" + output.Name + "/Columns", "ClassId", string.Empty,
                                "RowsetDescriptorType", string.Empty, "ContainsMappings", "False",
                                "ContainsCollections", "False" });

                        foreach (IDTSOutputColumn90 outCol in
                            output.OutputColumnCollection)
                        {
                            // Add the 'Column' node.
                            XmlElement xCol = AppendChildElement(xnodeOutputCols, "Node",
                                new string[]{ "Type", "Column", "Name", outCol.Name,
                                    "Uri", Uri + "/Output=" + output.Name + "/Column=" + outCol.Name, 
                                    "ClassId", 
                                    "Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSOutputColumn90",
                                    "RowsetDescriptorType", string.Empty, "ContainsMappings", "False",
                                    "ContainsCollections", "False" });

                            // Append column properties.
                            foreach (IDTSCustomProperty90 prop in outCol.CustomPropertyCollection)
                                AppendChildElement(xCol, "Property",
                                    new string[]{"Name", prop.Name, "Value", 
                                prop.Value == null ? string.Empty :prop.Value.ToString()});
                        }
                    }
                }
            }

            #endregion

            return xComponent;
        }

        #endregion

        #region Helper Methods

        protected object ConvertToCompatibleType(string value, string type)
        {
            object objValue = null;
            switch (type)
            {
                case "Boolean":
                    objValue = (object)(Convert.ToBoolean(value));
                    break;

                case "String":
                    objValue = (object)value;
                    break;

                case "DateTime":
                    objValue = (object)(Convert.ToDateTime(value));
                    break;

                case "Int32":
                    objValue = (object)(Convert.ToInt32(value));
                    break;
            }

            return objValue;
        }

        protected void UpdateNodeProperties(XmlDocument xmlDoc,
            XmlNode parentNode, string name, string value)
        {
            if (parentNode.SelectSingleNode("./Properties") == null)
                AddChildNode(xmlDoc, parentNode, "Properties");

            XmlNode propertiesNode = parentNode.SelectSingleNode("./Properties");

            XmlNode propertyNode = AddChildNode(xmlDoc, propertiesNode, "Property");
            AddAttribute(xmlDoc, propertyNode, "Name", name);
            AddAttribute(xmlDoc, propertyNode, "Value", value);
        }

        protected XmlNode UpdateNodeMappings(XmlDocument xmlDoc, XmlNode parentNode,
            bool preservePreviousMappings)
        {
            if (parentNode.SelectSingleNode("./Mappings") == null)
                AddChildNode(xmlDoc, parentNode, "Mappings");

            XmlNode mappingsNode = parentNode.SelectSingleNode("./Mappings");

            string preservePreviousMappingsValue;
            if (preservePreviousMappings)
                preservePreviousMappingsValue = "True";
            else
                preservePreviousMappingsValue = "False";

            AddAttribute(xmlDoc, mappingsNode, "PreservePreviousMappings", preservePreviousMappingsValue);

            return mappingsNode;
        }

        protected void UpdateNodeMapping(XmlDocument xmlDoc, XmlNode parentNode,
            bool preservePreviousMappings, string usage, string sourceColumn, string destinationColumn)
        {
            XmlNode mappingsNode = UpdateNodeMappings(xmlDoc, parentNode, preservePreviousMappings);

            XmlNode mappingNode = AddChildNode(xmlDoc, mappingsNode, "Mapping");
            AddAttribute(xmlDoc, mappingNode, "Usage", usage);
            AddAttribute(xmlDoc, mappingNode, "SourceColumn", sourceColumn);
            AddAttribute(xmlDoc, mappingNode, "DestinationColumn", destinationColumn);
        }

        protected void UpdateNodeCollectionProperties(XmlDocument xmlDoc,
            XmlNode parentNode, bool preservePreviousCollections,
            string collectionName, string collectionType,
            string propertyName, string propertyValue)
        {
            XmlNode collectionNode = UpdateNodeCollection(xmlDoc, parentNode,
                preservePreviousCollections, collectionName, collectionType);

            if (collectionNode.SelectSingleNode("./Properties") == null)
                AddChildNode(xmlDoc, collectionNode, "Properties");

            XmlNode propertiesNode = collectionNode.SelectSingleNode("./Properties");

            XmlNode propertyNode = AddChildNode(xmlDoc, propertiesNode, "Property");
            AddAttribute(xmlDoc, propertyNode, "Name", propertyName);
            AddAttribute(xmlDoc, propertyNode, "Value", propertyValue);
        }

        protected XmlNode UpdateNodeCollection(XmlDocument xmlDoc, XmlNode parentNode,
            bool preservePreviousCollections, string collectionName, string collectionType)
        {
            XmlNode collectionsNode = UpdateNodeCollections(xmlDoc, parentNode, preservePreviousCollections);

            XmlNode collectionNode;
            if (parentNode.SelectSingleNode("./CollectionInformation/Collection[@Name='"
                + collectionName + "' and @Type='" + collectionType + "']") == null)
            {
                //XmlNode collectionsNode = parentNode.SelectSingleNode("./CollectionInformation");
                collectionNode = AddChildNode(xmlDoc, collectionsNode, "Collection");
                AddAttribute(xmlDoc, collectionNode, "Name", collectionName);
                AddAttribute(xmlDoc, collectionNode, "Type", collectionType);
            }

            collectionNode = parentNode.SelectSingleNode("./CollectionInformation/Collection[@Name='"
                + collectionName + "' and @Type='" + collectionType + "']");

            return collectionNode;
        }

        protected XmlNode UpdateNodeCollections(XmlDocument xmlDoc, XmlNode parentNode, bool preservePreviousCollections)
        {
            if (parentNode.SelectSingleNode("./CollectionInformation") == null)
                AddChildNode(xmlDoc, parentNode, "CollectionInformation");

            XmlNode collectionsNode = parentNode.SelectSingleNode("./CollectionInformation");

            string preservePreviousCollectionsValue;
            if (preservePreviousCollections)
                preservePreviousCollectionsValue = "True";
            else
                preservePreviousCollectionsValue = "False";

            AddAttribute(xmlDoc, collectionsNode, "PreservePreviousCollections", preservePreviousCollectionsValue);

            return collectionsNode;
        }

        protected void UpdateNodeCollectionColumnProperties(XmlDocument xmlDoc,
            XmlNode parentNode, bool preservePreviousCollections,
            string collectionName, string collectionType,
            string columnName, string propertyName, string propertyValue)
        {
            XmlNode collectionNode = UpdateNodeCollection(xmlDoc, parentNode,
                preservePreviousCollections, collectionName, collectionType);

            if (collectionNode.SelectSingleNode("./Columns") == null)
                AddChildNode(xmlDoc, collectionNode, "Columns");

            XmlNode columnNode;
            if (collectionNode.SelectSingleNode("./Columns/Column[@Name='" + columnName + "']") == null)
            {
                XmlNode columnsNode = collectionNode.SelectSingleNode("./Columns");
                columnNode = AddChildNode(xmlDoc, columnsNode, "Column");
                AddAttribute(xmlDoc, columnNode, "Name", columnName);
            }

            columnNode = collectionNode.SelectSingleNode("./Columns/Column[@Name='" + columnName + "']");

            if (columnNode.SelectSingleNode("./Properties") == null)
                AddChildNode(xmlDoc, columnNode, "Properties");

            XmlNode propertiesNode = columnNode.SelectSingleNode("./Properties");

            XmlNode propertyNode = AddChildNode(xmlDoc, propertiesNode, "Property");
            AddAttribute(xmlDoc, propertyNode, "Name", propertyName);
            AddAttribute(xmlDoc, propertyNode, "Value", propertyValue);
        }

        protected XmlNode AddChildNode(XmlDocument xmlDoc, XmlNode parentNode, string name)
        {
            parentNode.AppendChild(xmlDoc.CreateNode(XmlNodeType.Element, name, null));
            return parentNode.LastChild;
        }

        protected void AddAttribute(XmlDocument xmlDoc, XmlNode parentNode, string name, string value)
        {
            XmlNode xmlAttrib = xmlDoc.CreateAttribute(name);
            xmlAttrib.InnerText = value;
            parentNode.Attributes.Append((XmlAttribute)xmlAttrib);
        }

        #endregion
    }
}
